package controller

import (
	"bytes"
	"context"
	"encoding/json"
	"encoding/xml"
	"io"
	"log"
	"net/http"
	"sale-admin/config"
	"sale-admin/config/mysql"
	"sale-admin/config/redis"
	"sale-admin/internal/app/web/schema"
	"sale-admin/internal/app/web/service"
	"sale-admin/internal/app/web/service/order"
	payService "sale-admin/internal/app/web/service/pay"
	"sale-admin/internal/app/web/service/redemption"
	"sale-admin/internal/dal"
	"sale-admin/internal/dao"
	"sale-admin/internal/define"
	"sale-admin/internal/models"
	"sale-admin/internal/pkg"
	"sort"
	"strconv"
	"strings"
	"time"

	"192.168.1.75/go-pkg/errorx"
	"192.168.1.75/go-pkg/helper"
	"192.168.1.75/go-pkg/logx"
	"192.168.1.75/go-pkg/response"
	"github.com/gin-gonic/gin"
	"github.com/gin-gonic/gin/binding"
	"github.com/go-pay/gopay"
	wechatv3 "github.com/go-pay/gopay/wechat/v3"
	"github.com/shopspring/decimal"
	miniOrder "github.com/silenceper/wechat/v2/miniprogram/order"
	"github.com/stripe/stripe-go/v74"
	"github.com/stripe/stripe-go/v74/checkout/session"
	"github.com/stripe/stripe-go/v74/refund"
)

type Pay struct {
	wechatPayService payService.WechatPay
}

var AliPayAppID = "2019110769005215"
var AliPayPrivateKey = "MIIEowIBAAKCAQEAhN24sCICY2/6T3BSuz+UEQqkqciKMAkGkYml0O7uosUvuYW2WjQrwaMs3M47mK9cn8W7cxMqFzpOnKshOAuIxxYlWoUD0fFVc/xdcQ2MHMjx0WTV7Lm/rzskV0DQFdMvWeGqCWaZw+xAzApbsjusMOsVTZVzuF9EG+QvD7tdMfnNw1kmug8xeOZVRaC6XWxPvMSLyD13xithlklaXqFLS3BWXXIFaYM37vla5plKKwUoJp6SdDNDqHNWZ/UsolDmbov2CPuV5Fw/pDETC94mWLjoZBWDfHD9eE8Tf5+u4x/6KVbqSA/Ruhrcc/+ZTLbYDDJVLIafuRVsGePRxg4l3QIDAQABAoIBACVrMC1ilpucmLkwoAweHrwh7Dfow4o9sHzicDGEaO6sbAFqt+11jfEt3F+fMUeR85sHnAAPoKYEBMm4pFVdYjr3cLOqjGXe+rr8kvBY6WHd8cSQCnNt6J8YH+b22OOco1RD6phVLe7wpVsRe7uZdKgePNSpx3pgUhUs0Vgz405EbGGfRIMyZFMMfEuMIo5T06Bgekdd++YetvQNzLFC0Ev4pwEGr9FpR0jjP7lNq95Vmb13Jvxt0uSSZdhlPabRBIThJV/ZP3uOXOzr8w8HkjovDgmn89OgXBubeh6vHmpM1DXpF7cCd9tu/eJkjx9Je1/RI3XLG8F343O2uR/gFyECgYEAz7izEMWmIN8Sl/3NFr9uOIi8ED18PzGXeBez7XkrQdrBBdDccqPtzMK0ia0iSiw94rzOp8AA0S0URy5GiM7YBvkGjUZyx81jbU42yIb5NDtJ0l+/cwAd7bq12X5OzYoQr4sQ+xs2hPzbjETIkBo1txrZc5zlkNF//iat/Sdf5+kCgYEAo78sNSDS31eWFQWwL49QDK/ZkJzhGsk4V0BoVle+VXcNSOW3uT78sw8peIugLYfWA5XEl7KB7qCi3U9N7+cTPf+6Kt8nUlDeKBq9hsx7oG//owm7VkWXLl2/ih3QzXdcylZoedjHngTuEl/BDMckQmSrda43+I1LYNWfey1jCdUCgYBPX+G1EAHKX9OIFujOv3nLypwqjIQ01aturYgIv5uC/6dwpH4IpJGTA/ffADSxKTTAtqs1OM5XICW2iX1Ue83a5D/XrkrgBKDwvMl8WO9TQTCVXhcXR70oAYfJw1Uzf4WevsJPYfe0W7c9Y29MeWd7P5xGbHbgnt2M7ZTgvrIMOQKBgDwgmdoUaHpsjgbAJPcB1/IgrHJGuP39/3tCgk2aQKDfa3hmKfWS5HXXznGeS74MUBDW8O6HNODsCYHcehDquA0fFzxg3u6CFWGCmC+kbIz4M1Wp4CmIuj/jWs8bra2U8/S0j/6WFI643rbOhHPtuCnND4zR4WmacKBpMPupKeHVAoGBAIZXHLVMDYcbqsmWMcSYlWEIhAaF00Ji2vQr2KRNZK9ZR5nujD3v1MgNjE9y2hM124U+SP5lHDA3I33zv7USm3EM4kb/0swN6z8HqBwnr+gpW4CSj1A0gB+dLfVE/O207wPNP8nt48wRe8KG8kQlOyVTf+h6gYCBJN3ax3/v0pCB"

func (t *Pay) Init(c config.ConfigType) {
	err := t.wechatPayService.V3ConfigInit("", "", "")
	if err != nil {
		logx.Fatal(context.Background(), "WechatPay ConfigInit error", logx.Err(err))
	}

	ctxWithTimeout, cancel := context.WithTimeout(context.Background(), 15*time.Second)
	defer cancel()
	err = t.wechatPayService.SetComplaintNotifyUrl(ctxWithTimeout, c.WechatComplaintNotifyUrl)
	if err != nil {
		//这里调用fatal出发panic
		logx.Error(context.Background(), "SetComplaintNotifyUrl error", logx.Err(err))
	}
}

// 微信支付回调处理函数
func (Pay) WechatNotify(c *gin.Context) {

	// 读取HTTP请求体
	body, err := io.ReadAll(c.Request.Body)
	if err != nil {
		c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"msg": "failed to read request body"})
		return
	}

	// 将 HTTP请求体解析为map
	params, err := parseXml(body)
	if err != nil {
		c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"msg": "failed to parse request body"})
		return
	}

	// 验证签名
	if !verifySignature(params) {
		c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"msg": "invalid signature"})
		return
	}
	// 接收微信回调回来的商户订单号
	outTradeNo, ok := params["out_trade_no"]
	db := mysql.NewDB()

	// 2、执行写入回调日志
	msgBytes, _ := json.Marshal(params)
	var uniqueID string
	if _, ok := params["openid"]; ok {
		uniqueID = params["openid"]
	}

	// 更改当前的订单的支付方式为微信
	if ok && outTradeNo != "" {
		// 查询订单表
		order.OrderNotifyService{}.SaveNotifyLog(c.Request.Context(), db, outTradeNo, "WeChat", string(msgBytes), uniqueID)
	} else {
		order.OrderNotifyService{}.SaveNotifyLog(c.Request.Context(), db, "null", "WeChat", string(msgBytes), uniqueID)
	}

	// 3、返回处理结果
	c.XML(http.StatusOK, gin.H{"return_code": "SUCCESS", "return_msg": "OK"})

}

// 微信支付回调处理函数
// post请求，json格式
// 微信文档参考: https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_5.shtml
func (Pay) WechatV3Notify(c *gin.Context) {

	//payService.WechatPay{}.V3ConfigInit("", "", "")
	resource, err := wechatv3.V3ParseNotify(c.Request)
	if err != nil {
		c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{
			// 错误码，SUCCESS为清算机构接收成功，其他错误码为失败。
			"code": "SUCCESS",
			"msg":  "failed to parse request body",
		})
		return
	}
	result, err := resource.DecryptPayCipherText(config.Config.WechatV3Config.APIv3Key)
	if err != nil {
		c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{
			// 错误码，SUCCESS为清算机构接收成功，其他错误码为失败。
			"code": "SUCCESS",
			"msg":  "failed to parse request body",
		})
		return
	}

	logx.Info(context.Background(), "WechatV3Notify", logx.Any("result.OutTradeNo", result.OutTradeNo), logx.Any("config.Config.WechatMini.AppID", config.Config.WechatMini.AppID), logx.Any("result.Payer.Openid", result.Payer.Openid), logx.Any("config.Config.WechatV3Config.MchID", config.Config.WechatV3Config.MchID))
	outTradeNo := result.OutTradeNo
	msgBytes, _ := json.Marshal(result)
	uniqueID := result.Payer.Openid
	if result.Appid == config.Config.WechatMini.AppID { // 小程序支付的订单改自提
		// 小程序发货信息录入
		mp, err := pkg.MiniProgram{}.New()
		if err != nil {
			return
		}
		timeNow := time.Now()
		var shipping = &miniOrder.UploadShippingInfoRequest{
			OrderKey: &miniOrder.ShippingOrderKey{
				OrderNumberType: 1, // 商户侧单号
				Mchid:           config.Config.WechatV3Config.MchID,
				OutTradeNo:      result.OutTradeNo,
			},
			LogisticsType: miniOrder.LogisticsTypeSelfPickup,     // 用户自提
			DeliveryMode:  miniOrder.DeliveryModeUnifiedDelivery, // 同一发货
			ShippingList: []*miniOrder.ShippingInfo{
				{
					ItemDesc: "大道售卖商品",
				},
			},
			UploadTime: &timeNow,
			Payer: &miniOrder.ShippingPayer{
				Openid: result.Payer.Openid,
			},
		}
		err = mp.GetShipping().UploadShippingInfo(shipping)
		logx.Info(context.Background(), "wechat_v3_notify miniapp", logx.Any("upload_shipping_info", err))
	}
	//写入订单回调日志
	db := mysql.NewDB()
	if result.TradeState == "SUCCESS" {
		if outTradeNo != "" {
			order.OrderNotifyService{}.SaveNotifyLog(c.Request.Context(), db, outTradeNo, "WeChat", string(msgBytes), uniqueID)
		} else {
			order.OrderNotifyService{}.SaveNotifyLog(c.Request.Context(), db, "null", "WeChat", string(msgBytes), uniqueID)
		}
	}

	// 返回处理结果
	c.XML(http.StatusOK, nil)
}

// 微信支付投诉通知回调处理函数
// post请求，json格式
// 微信文档参考: https://pay.weixin.qq.com/doc/v3/merchant/4012289719
func (t Pay) WechatV3ComplaintNotify(c *gin.Context) {
	logx.Info(context.Background(), "WechatV3ComplaintNotify")

	detailRsp, err := t.wechatPayService.V3ComplaintNotify(c)
	if err != nil {
		logx.Error(context.Background(), "WechatV3ComplaintNotify", logx.Any("err", err))
		c.XML(http.StatusInternalServerError, nil)
		return
	}

	if detailRsp.Response != nil {
		logx.Info(context.Background(), "WechatV3ComplaintNotify success",
			logx.Any("ComplaintId", detailRsp.Response.ComplaintId))
	}

	//t.wechatPayService.V3ComplaintNotify(c.Request.Context(), result.OutTradeNo, result.TotalAmount, result.RefundAmount)

	// 返回处理结果
	c.JSON(http.StatusOK, &wechatv3.EmptyRsp{})
}

// 解析 XML 数据为 map
func parseXml(data []byte) (res map[string]string, err error) {
	// 将 XML 数据解析为 map
	result := make(map[string]string)
	decoder := xml.NewDecoder(bytes.NewReader(data))
	var name string
	var text bool
	for t, err1 := decoder.Token(); err1 == nil; t, err1 = decoder.Token() {
		switch token := t.(type) {
		case xml.StartElement:
			name = token.Name.Local
			text = true
		case xml.EndElement:
			text = false
		case xml.CharData:
			if text {
				content := string([]byte(token))
				result[name] = strings.TrimSpace(content)
			}
		default:
			return
		}
	}
	return result, nil
}

// 验证签名
func verifySignature(params map[string]string) bool {
	// 按照参数名的字典序排序
	var keys []string
	for key := range params {
		if key != "sign" && params[key] != "" {
			keys = append(keys, key)
		}
	}
	sort.Strings(keys)

	// 拼接参数值
	var values []string
	for _, key := range keys {
		values = append(values, key+"="+params[key])
	}
	data := strings.Join(values, "&")
	var OrderInfo models.MaOrder
	var OrderCount int64
	db := mysql.NewDB()
	db.Model(&models.MaOrder{}).
		Where("out_trade_no = ?", params["out_trade_no"]).
		Take(&OrderInfo).Count(&OrderCount)
	if OrderCount == 0 {
		return false
	}

	paymentMap := service.RobotPayment{}.GetRobotPayments(context.Background(), db, OrderInfo.SalesID, 0)
	var MerchantKey string
	if _, ok := paymentMap["1"]; ok {
		wechatConfig, _ := dao.Payment{}.GetWechatPay(context.Background(), dal.Q, OrderInfo.MerchantID, paymentMap["1"])
		if len(wechatConfig) == 0 {
			return false
		}
		MerchantKey = wechatConfig[0].WeChatAppKey
	}
	if MerchantKey == "" {
		logx.Error(context.Background(), "获取微信支付秘钥失败", logx.Any("shop_id", OrderInfo.MerchantID), logx.Any("out_trade_no", params["out_trade_no"]))
		return false
	}
	// 添加商户密钥，计算签名值
	data += "&key=" + MerchantKey
	sign := strings.ToUpper(helper.Md5(data))

	// 验证签名是否正确
	return sign == params["sign"]
}

// 支付宝支付
func (Pay) AliPayNativePay(c *gin.Context) {
	err := payService.Alipay{}.ConfigInit(AliPayAppID, AliPayPrivateKey)
	if err != nil {
		response.JSON(c, nil, errorx.New("支付宝配置错误", -1))
	}
	orderNo, _ := helper.UUID{}.String()
	res, err := payService.Alipay{}.DoPay(orderNo, 0.01)
	response.JSON(c, res, err)
}

// 支付宝查询订单
func (Pay) AliPayQuery(c *gin.Context) {
	var params struct {
		OutTradeNo string `json:"out_trade_no"`
	}
	c.ShouldBindBodyWith(&params, binding.JSON)
	err := payService.Alipay{}.ConfigInit(AliPayAppID, AliPayPrivateKey)
	if err != nil {
		response.JSON(c, nil, errorx.New("支付宝配置错误", -1))
	}
	res, err := payService.Alipay{}.AliPayQuery(params.OutTradeNo)
	response.JSON(c, res, err)
}

// 支付宝刷脸初始化
func (Pay) AliPayMicropayInit(c *gin.Context) {
	var params struct {
		MetaInfo interface{} `json:"meta_info"`
	}
	c.ShouldBindBodyWith(&params, binding.JSON)
	err := payService.Alipay{}.ConfigInit(AliPayAppID, AliPayPrivateKey)
	if err != nil {
		response.JSON(c, nil, errorx.New("支付宝配置错误", -1))
	}
	data, err := payService.Alipay{}.AliPayMicropayInit(params.MetaInfo)
	response.JSON(c, data, err)
}

// 支付宝刷脸支付
func (Pay) AliPayMicropay(c *gin.Context) {
	var params struct {
		AuthCode string `json:"auth_code"`
	}
	c.ShouldBindBodyWith(&params, binding.JSON)
	err := payService.Alipay{}.ConfigInit(AliPayAppID, AliPayPrivateKey)
	if err != nil {
		response.JSON(c, nil, errorx.New("支付宝配置错误", -1))
	}
	orderNo, _ := helper.UUID{}.String()
	data, err := payService.Alipay{}.AliPayMicropay(orderNo, params.AuthCode, "security_code", 0.01)
	response.JSON(c, data, err)
}

// AlipayRefund 支付宝退款
func (Pay) AlipayRefund(c *gin.Context) {
	var params struct {
		OrderNo      string  `json:"order_no"`
		RefundAmount float64 `json:"refund_amount"`
	}
	c.ShouldBindBodyWith(&params, binding.JSON)
	err := payService.Alipay{}.ConfigInit(AliPayAppID, AliPayPrivateKey)
	if err != nil {
		response.JSON(c, nil, errorx.New("支付宝配置错误", -1))
	}
	data, err := payService.Alipay{}.AliPayRefund(params.OrderNo, params.RefundAmount, "测试退款", "")
	response.JSON(c, data, err)
}

// AlipayNotify 支付宝回调
func (Pay) AlipayNotify(c *gin.Context) {
	var err error
	if err = c.Request.ParseForm(); err != nil {
		c.Writer.WriteString("fail")
		return
	}
	var form map[string][]string = c.Request.Form
	params := make(gopay.BodyMap, len(form)+1)
	for k, v := range form {
		if len(v) == 1 {
			params.Set(k, v[0])
		}
	}

	var outTradeNo string
	if _, ok := params["out_trade_no"]; ok {
		outTradeNo = params["out_trade_no"].(string) //商户订单号(人脸支付后缀F2)
	}
	msgBytes, _ := json.Marshal(params)
	var unique_id string
	if _, ok := params["buyer_id"]; ok { //buyer_id作为唯一值
		unique_id = params["buyer_id"].(string)
	}
	if _, ok := params["trade_status"]; ok { //交易状态
		if params["trade_status"].(string) == "TRADE_SUCCESS" {

			db := mysql.NewDB()
			// 写入订单回调日志
			order.OrderNotifyService{}.SaveNotifyLog(
				c.Request.Context(), db,
				outTradeNo, // 订单号
				"Alipay",   // 支付宝
				string(msgBytes),
				unique_id,
			)
			c.Writer.WriteString("success")
			return
		}
	}
	c.Writer.WriteString("fail")
}

// 大兴支付
func (Pay) DaxingPay(c *gin.Context) {
	orderNo, _ := helper.UUID{}.String()
	payService.DaxingPay{}.DaxingInit("", "", "", "", "", "")
	res, err := payService.DaxingPay{}.CreateOrder(c.Request.Context(), "ALI_20", orderNo, 1, "")
	response.JSON(c, res, err)
}

// 大兴查询
func (Pay) DaxingQuery(c *gin.Context) {
	var params struct {
		OrderNo       string  `json:"order_no"`
		PaymentAmount float64 `json:"payment_amount"`
	}
	c.ShouldBindBodyWith(&params, binding.JSON)

	payService.DaxingPay{}.DaxingInit("", "", "", "", "", "")
	res, err := payService.DaxingPay{}.QueryOrder(c.Request.Context(), "WX", params.OrderNo, params.PaymentAmount)
	response.JSON(c, res, err)
}

// 大兴退款
func (Pay) DaxingRefund(c *gin.Context) {
	var params struct {
		OrderNo       string  `json:"order_no"`
		PaymentAmount float64 `json:"payment_amount"`
	}
	c.ShouldBindBodyWith(&params, binding.JSON)

	payService.DaxingPay{}.DaxingInit("", "", "", "", "", "")
	res, err := payService.DaxingPay{}.Refund(c.Request.Context(), params.OrderNo, "WX", params.PaymentAmount)
	response.JSON(c, res, err)
}

// SendCms 发送短信验证码
func (Pay) SendCms(c *gin.Context) {
	var params struct {
		Phone string `json:"phone"`
	}
	c.ShouldBindBodyWith(&params, binding.JSON)

	templateCode := config.Config.SMSTemplateCode
	rds := redis.NewDB()
	err := helper.SendSMSCode(rds, params.Phone, templateCode, 5, 180)
	if err != nil {
		response.JSON(c, nil, errorx.New("短信发送频繁，请稍后再试"))
		return
	}
	response.JSON(c, nil, nil)
}

// Xinzhe 新哲教育
func (Pay) Xinzhe(c *gin.Context) {

	// 新哲教育提交参数
	var params struct {
		Phone       string `json:"phone"`
		Name        string `json:"name"`
		Grade       int    `json:"grade"`
		GradeDetail int    `json:"grade_detail"`
		Abroad      int    `json:"abroad"`
		Sex         int    `json:"sex"`
		Code        string `json:"code"`
	}
	c.ShouldBindBodyWith(&params, binding.JSON)

	var saveData models.MaPersonInfo
	saveData.Phone = params.Phone
	saveData.Name = params.Name
	saveData.Grade = strconv.Itoa(params.Grade)
	saveData.GradeDetail = params.GradeDetail
	saveData.Abroad = params.Abroad
	saveData.Sex = params.Sex
	// 验证码是否成功
	rds := redis.NewDB()
	exist, _ := helper.CheckSMSCode(rds, params.Phone, params.Code)
	if params.Code == "" {
		response.JSON(c, nil, errorx.New("短信验证码不能为空"))
		return
	}
	if exist {
		db := mysql.NewDB()
		db.Model(&models.MaPersonInfo{}).Create(&saveData)
		// 并且返回注册资料后的兑换码
		Title := "新哲教育"
		codeList, _ := redemption.RedemptionService{}.Add(c.Request.Context(), db, "1", 1, 99, "2023-10-01 23:59:59", Title)
		var RedemptionCode string
		if len(codeList) == 1 {
			RedemptionCode = codeList[0]
		}
		response.JSON(c, gin.H{"redemption_code": RedemptionCode}, nil)
		return
	} else {
		response.JSON(c, nil, errorx.New("短信验证码错误"))
		return
	}

}

// Look 调查问卷
func (Pay) Look(c *gin.Context) {
	//
}

func StripeHtml(str string) string {
	html := "<html><body><div style=\"font-size: 36px;text-align: center;line-height: 300px;height: 300px;\">"
	html += str
	html += "</div></body></html>"
	return html
}

// StripePay stripe支付 创单时提供链接跳转到此处
func (Pay) StripePay(c *gin.Context) {
	OrderID := c.Query("order_id")

	//获取订单信息
	var orderInfo models.MaOrder
	var Count int64
	mysql.NewDB().Model(&models.MaOrder{}).
		Where("out_trade_no = ?", OrderID).
		Take(&orderInfo).
		Count(&Count)
	if Count == 0 {
		c.Writer.WriteString(StripeHtml("This order does not exist!"))
		return
	}
	if orderInfo.End == 1 {
		c.Writer.WriteString(StripeHtml("This order was closed!"))
		return
	}

	//获取机器信息
	var RobotInfo models.MaRobot
	mysql.NewDB().Model(&models.MaRobot{}).
		Where("sales_id = ?", orderInfo.SalesID).
		Where("deleted = 0").
		Take(&RobotInfo)
	if RobotInfo.ID == "" {
		c.Writer.WriteString(StripeHtml("Robot does not exist!"))
		return
	}
	//获取支付配置
	var paymentID int
	if RobotInfo.PaymentType1 == "8" {
		paymentID = RobotInfo.Payment1
	} else if RobotInfo.PaymentType2 == "8" {
		paymentID = RobotInfo.Payment2
	} else if RobotInfo.PaymentType3 == "8" {
		paymentID = RobotInfo.Payment3
	} else if RobotInfo.PaymentType4 == "8" {
		paymentID = RobotInfo.Payment4
	}
	if paymentID == 0 {
		c.Writer.WriteString(StripeHtml("Stripe payment configuration does not exist!"))
		return
	}
	StripeInfo, _ := dao.Payment{}.GetStripePay(context.Background(), dal.Q, orderInfo.MerchantID, paymentID)
	if len(StripeInfo) == 0 {
		c.Writer.WriteString(StripeHtml("Stripe payment configuration does not exist!"))
		return
	}
	var CountData []schema.OrderGoodsDetailOld
	json.Unmarshal([]byte(orderInfo.CountData), &CountData)
	//获取stripe商品信息
	var GoodsInfo models.MaGoodsInfo
	mysql.NewDB().Model(&models.MaGoodsInfo{}).
		Where("merchant_id = ?", orderInfo.MerchantID).
		Where("goods_id = ? and deleted = 0", CountData[0].GoodsID).
		Take(&GoodsInfo)
	if GoodsInfo.ID == "" || GoodsInfo.ExternalProductID == "" {
		c.Writer.WriteString(StripeHtml("There is an exception in the product information!"))
		return
	}
	stripe.Key = StripeInfo[0].StripePrivKey //服务端使用私钥，网页端使用公钥
	// stripe.Key = "sk_test_4eC39HqLyjWDarjtT1zdp7dc" //这个是测试用的私钥
	// Product: stripe.String("prod_OAbUJGHxph0Q9V") //这个是测试用的产品id
	// visa: 4242424242424242 //测试使用的银行卡，其他信息随意填写
	PaymentAmount := decimal.NewFromFloat(orderInfo.PaymentAmount).Mul(decimal.NewFromInt(100)).IntPart()
	var params stripe.CheckoutSessionParams
	item := stripe.CheckoutSessionLineItemParams{
		PriceData: &stripe.CheckoutSessionLineItemPriceDataParams{
			Product:    stripe.String(GoodsInfo.ExternalProductID), //产品id
			UnitAmount: stripe.Int64(int64(PaymentAmount)),         //单价 2990 代表 29.90元 //美元限制最低$0.50美元，约为¥3.6元
			Currency:   stripe.String(string(stripe.CurrencySGD)),  //汇率 CurrencyCNY
		},
		Quantity: stripe.Int64(1),
	}

	params.LineItems = []*stripe.CheckoutSessionLineItemParams{&item}
	params.SuccessURL = stripe.String(config.Config.BaseUrl + "/admin/pay/stripe-success?order_id=" + OrderID + "&session_id={CHECKOUT_SESSION_ID}")
	params.CancelURL = stripe.String(config.Config.BaseUrl + "/admin/pay/stripe-cancel?order_id=" + OrderID + "&session_id={CHECKOUT_SESSION_ID}")
	params.Mode = stripe.String(string(stripe.CheckoutSessionModePayment))
	params.CustomerEmail = stripe.String("sales@dadaoii.com")
	// params.PaymentMethodTypes = stripe.StringSlice([]string{
	// 	"alipay",
	// 	"card",
	// })
	s, err := session.New(&params)

	if err != nil {
		log.Printf("session.New: %v", err)
		if stripeErr, ok := err.(*stripe.Error); ok {
			if stripeErr.Type == "amount_too_small" {
				c.Writer.WriteString(StripeHtml("Amount too small!"))
				return
			}
		} else {
			c.Writer.WriteString(StripeHtml(stripeErr.Error()))
			return
		}
	}
	c.Redirect(http.StatusSeeOther, s.URL)
}

// 支付成功回调
// http://localhost:3021/admin/pay/stripe-success?order_id=123456789&session_id=cs_test_a1WKKvwD5kJprYYfeAbDCO2I5hunaRBTRLATFCZHnVddkn1gkUbkv3Wkx6
func (Pay) StripeSuccess(c *gin.Context) {
	db := mysql.NewDB()
	var orderInfo models.MaOrder
	db.Model(&models.MaOrder{}).
		Where("out_trade_no = ?", c.Query("order_id")).
		Take(&orderInfo)
	StripeInfo, _ := dao.Payment{}.GetStripePay(context.Background(), dal.Q, orderInfo.MerchantID, 0)
	if len(StripeInfo) == 0 {
		c.Writer.WriteString(StripeHtml("stripe pay config error"))
		return
	}
	stripe.Key = StripeInfo[0].StripePrivKey
	s, _ := session.Get(c.Query("session_id"), nil)
	if s.Status == "complete" { //支付成功
		if orderInfo.MerchantID != "" && orderInfo.End != 1 { //订单未关闭
			if StripeInfo[0].ID != 0 {
				db.Model(&models.MaOrder{}).
					Where("out_trade_no = ?", c.Query("order_id")).
					Updates(map[string]interface{}{
						"end_out_trade_no": s.PaymentIntent.ID,
						"order_status":     2, //支付成功
						"payment_id":       8,
						"modify_time":      time.Now(),
					})
			}
			c.Writer.WriteString(StripeHtml("Thanks for your order, " + s.CustomerDetails.Name + "!"))
			return
		}
		if orderInfo.MerchantID != "" && orderInfo.End == 1 { // 订单已经关闭
			//判断当前订单状态是否需要退款
			var OrderCount int64
			var OrderInfo models.MaOrder
			mysql.NewDB().Model(&models.MaOrder{}).
				Where("out_trade_no = ?", c.Query("order_id")).
				Where("end = 1").                 //订单已结束
				Where("order_status in (2,3,5)"). //2已支付(end_type =2 订单关闭时为2已支付) || 3未支付(end_type =2 订单关闭时为3未支付) || 5出货失败(关闭订单时尝试退款但未成功)
				Take(&OrderInfo).Count(&OrderCount)
			if OrderCount > 0 {
				//订单需退款
				PaymentTypeMap := service.RobotPayment{}.GetRobotPayments(context.Background(), db, OrderInfo.SalesID, 0)
				var RobotInfo models.MaRobot
				db.Model(&models.MaRobot{}).
					Where("sales_id = ? and deleted = 0", OrderInfo.SalesID).
					Take(&RobotInfo)
				//生成退款单号
				orderRefundNo, _ := helper.UUID{}.String()
				if _, ok := PaymentTypeMap["8"]; ok {
					StripePayConfig, _ := dao.Payment{}.GetStripePay(context.Background(), dal.Q, RobotInfo.MerchantID, PaymentTypeMap["8"])
					if len(StripePayConfig) > 0 {
						var refundCode int
						remarks := "自动退款"
						stripe.Key = StripePayConfig[0].StripePrivKey
						params := &stripe.RefundParams{PaymentIntent: stripe.String(s.PaymentIntent.ID)}
						result, err := refund.New(params)
						if err != nil {
							refundCode = 0
							if stripeErr, ok := err.(*stripe.Error); ok {
								switch stripeErr.Type {
								case stripe.ErrorType(stripe.ErrorCodeChargeAlreadyRefunded):
									logx.Error(context.Background(), "charge_already_refunded") //已退款
									refundCode = 1
								}
							}
							logx.Info(context.Background(), "stripe refund", logx.Any("err", err.Error()))
						} else if result.Status == "succeeded" {
							alipayRspJson, _ := json.Marshal(result)
							refundType := order.OrderRefund{}.CreateRefundOrder(db, OrderInfo.MerchantID, OrderInfo.RobotName, OrderInfo.SalesID, OrderInfo.PaymentAmount, OrderInfo.PaymentAmount,
								s.PaymentIntent.ID, orderRefundNo, string(alipayRspJson), "", OrderInfo.PaymentID, "3", remarks, "")
							if refundType == 1 {
								refundCode = 1
							} else {
								refundCode = 2
							}
						}
						//根据退款状态执行告警通知
						switch refundCode {
						case 0:
							orderInfo.Remarks = remarks + "_退款失败"
							orderInfo.OrderStatus = 5 //出货失败
						case 1:
							orderInfo.Remarks = remarks + "_退款成功"
							orderInfo.OrderStatus = 6 //退款成功

							//推送消息
							alarmMsg := map[string]interface{}{
								"alarm_type": 1001, // 告警类型
								"robot_id":   RobotInfo.RobotName,
								"battery":    "",
								"created_at": time.Now().Unix(),
							}
							jsonString, _ := json.Marshal(alarmMsg)
							rdb := redis.NewDB()
							rdb.LPush(c.Request.Context(), define.RedisAlarmNotifyList, string(jsonString))
						case 2:
							orderInfo.Remarks = remarks + "_退款成功，保存失败"
							orderInfo.OrderStatus = 6 //退款成功
						}
						orderInfo.ModifyTime = time.Now()
						orderInfo.EndOutTradeNo = s.PaymentIntent.ID
						db.Model(&models.MaOrder{}).Where("out_trade_no = ?", c.Query("order_id")).Updates(&orderInfo)
					}
				}
			}
		}
	}
}

// stripe_ex 支付成功跳转页，仅做为landing page,不做支付成功判定
func (Pay) StripeExSuccess(c *gin.Context) {
	c.Writer.WriteString(StripeHtml("Thanks for your order!"))
}

type StripeExWebhookParams struct {
	Event string `json:"event"`
	Data  struct {
		PaymentID   string `json:"payment_id"`
		Status      string `json:"status"`
		Amount      int    `json:"amount"`
		Currency    string `json:"currency"`
		RedirectURL string `json:"redirect_url"`
		UpdatedAt   int    `json:"updated_at"`
		CreatedAt   int    `json:"created_at"`
	} `json:"data"`
}

// stripe_ex webhook
func (Pay) StripeExWebhook(c *gin.Context) {
	bodyBytes, err := c.GetRawData()
	if err != nil {
		return
	}
	HeaderSignature := c.GetHeader("X-Signature") //签名信息
	GetSignature := pkg.StripeEx{}.GetSignature(bodyBytes)
	if HeaderSignature == GetSignature {
		params := StripeExWebhookParams{}
		json.Unmarshal(bodyBytes, &params)
		logx.Info(c.Request.Context(), "stripe ex webhook", logx.Any("bodyBytes", string(bodyBytes)))

		db := mysql.NewDB()
		if params.Event == "payment_intent.created" { // 创单成功
			var OrderDetailsInfo models.MaOrderDetails
			db.Model(&models.MaOrderDetails{}).
				Where("stripe_ex_create_result like (?)", "%"+params.Data.PaymentID+"%").
				Take(&OrderDetailsInfo)
			if OrderDetailsInfo.ID != "" {
				db.Model(&models.MaOrder{}).
					Where("order_id = ?", OrderDetailsInfo.ID).
					Updates(map[string]interface{}{
						"end_out_trade_no": params.Data.PaymentID, // stripe_ex的payment_id，订单标记
						"payment_id":       10,
						"modify_time":      time.Now(),
					})
			}
		} else if params.Event == "payment_intent.succeeded" { // 支付成功
			var orderInfo models.MaOrder
			db.Model(&models.MaOrder{}).
				Where("end_out_trade_no = ?", params.Data.PaymentID).
				Select("*,sum(payment_amount) as payment_amount").
				Take(&orderInfo)
			if orderInfo.MerchantID != "" && orderInfo.End != 1 { //订单未关闭
				db.Model(&models.MaOrder{}).
					Where("end_out_trade_no = ?", params.Data.PaymentID).
					Updates(map[string]interface{}{
						"order_status": 2, //支付成功
						"payment_id":   10,
						"modify_time":  time.Now(),
					})
				c.JSON(http.StatusOK, nil)
				return
			}
			if orderInfo.MerchantID != "" && orderInfo.End == 1 { // 订单已经关闭
				//判断当前订单状态是否需要退款
				var OrderCount int64
				var OrderInfo models.MaOrder
				db.Model(&models.MaOrder{}).
					Where("end_out_trade_no = ?", params.Data.PaymentID).
					Where("end = 1").                 //订单已结束
					Where("order_status in (2,3,5)"). //2已支付(end_type =2 订单关闭时为2已支付) || 3未支付(end_type =2 订单关闭时为3未支付) || 5出货失败(关闭订单时尝试退款但未成功)
					Take(&OrderInfo).Count(&OrderCount)
				if OrderCount > 0 {
					//订单需退款
					PaymentTypeMap := service.RobotPayment{}.GetRobotPayments(context.Background(), db, OrderInfo.SalesID, 0)
					if _, ok := PaymentTypeMap["10"]; ok {
						pkg.StripeEx{}.Refund(c.Request.Context(), params.Data.PaymentID, "自动退款")
					}
				}
			}
		} else if params.Event == "payment_intent.refunded" || params.Event == "payment_intent.reversed" { // 退款成功
			var orderInfo models.MaOrder
			db.Model(&models.MaOrder{}).
				Where("end_out_trade_no = ?", params.Data.PaymentID).
				Select("*,sum(payment_amount) as payment_amount").
				Take(&orderInfo)
			// Webhook 增加一个事件类型 payment_intent.reversed
			// 对已经付款成功的Payment,还未发生记账,马上发起退款, 会触发`payment_intent.reversed`事件。
			// 对已经付款成功的Payment,已记账,发起退款, 会触发`payment_intent.refunded`事件。
			// Payment 有可能会出现 `reversed`的状态, 然后变成 `refunded`
			var RobotInfo models.MaRobot
			db.Model(&models.MaRobot{}).
				Where("sales_id = ? and deleted = 0", orderInfo.SalesID).
				Take(&RobotInfo)

			//推送消息
			alarmMsg := map[string]interface{}{
				"alarm_type": 1001, // 告警类型
				"robot_id":   RobotInfo.RobotName,
				"battery":    "",
				"created_at": time.Now().Unix(),
			}
			jsonString, _ := json.Marshal(alarmMsg)
			rdb := redis.NewDB()
			rdb.LPush(c.Request.Context(), define.RedisAlarmNotifyList, string(jsonString))

			orderInfo.Remarks = strings.TrimSuffix(orderInfo.Remarks, "_退款失败") + "_退款成功"
			orderInfo.OrderStatus = 6 //退款成功
			orderInfo.ModifyTime = time.Now()
			db.Model(&models.MaOrder{}).
				Where("end_out_trade_no = ?", params.Data.PaymentID).
				Updates(&orderInfo)
			// 生成退款订单
			orderRefundNo, _ := helper.UUID{}.String()
			order.OrderRefund{}.CreateRefundOrder(db, orderInfo.MerchantID, orderInfo.RobotName, orderInfo.SalesID, orderInfo.PaymentAmount, orderInfo.PaymentAmount,
				orderInfo.EndOutTradeNo, orderRefundNo, "", "", orderInfo.PaymentID, "0", "自动退款", "")
		}
		// 返回 200 正常结束，否则5分钟重试一次，最多重试5次
		c.JSON(http.StatusOK, nil)
	}
}

// 永旺支付回调
func (Pay) YongWangPay(c *gin.Context) {
	var params struct {
		TokenId    string  `json:"tokenId"`    // 随机字符串
		Timestamp  int64   `json:"timestamp"`  // 时间戳
		PayCode    string  `json:"payCode"`    // 支付编码
		OutOrderNo string  `json:"outOrderNo"` // 外部订单号
		AppId      string  `json:"appId"`      // appID
		OpenId     string  `json:"openId"`     // 会员id
		State      string  `json:"state"`      // DONE : 订单支付完成
		PayPoint   float64 `json:"payPoint"`   // 支付积分
		PayTime    string  `json:"payTime"`    // 支付时间 格式:y-m-d h:i:s
	}
	c.ShouldBindBodyWith(&params, binding.JSON)
	// 查询订单是否存在

	// 修改订单状态为支付成功

	// 记录积分 OrderPoints

	// 返回结果
	var res struct {
		Code      int    `json:"code"`
		Message   string `json:"message"`
		Timestamp int64  `json:"timestamp"` // 时间戳
		Data      struct {
			OpenID     string `json:"openId"`
			NotifyFlag bool   `json:"notifyFlag"`
			OutTradeNo string `json:"outTradeNo"`
		} `json:"data"`
	}
	c.JSON(http.StatusOK, res)
}
